package com.wm.wmim.connect.center.rpc.impl;

import com.wm.wmim.common.dto.RpcRespDTO;
import com.wm.wmim.common.utils.RpcRespUtils;
import com.wm.wmim.connect.center.api.bo.RouteInfoBO;
import com.wm.wmim.connect.center.api.bo.req.RouteDeleteReqBO;
import com.wm.wmim.connect.center.api.bo.resp.RouteGetRespBO;
import com.wm.wmim.connect.center.factory.RouteRpcServiceFactory;
import com.wm.wmim.connect.center.manager.RouteManager;
import com.wm.wmim.connect.sdk.push.bo.req.PushWsMessageReqBO;
import com.wm.wmim.connect.sdk.push.enums.WsPushResultEnum;
import com.wm.wmim.connect.sdk.push.rpc.PushSdkService;
import com.wm.wmim.connect.sdk.push.bo.resp.PushWsMessageFailedInfoBO;
import com.wm.wmim.connect.sdk.push.bo.resp.PushWsMessageRespBO;
import com.wm.wmim.connect.sdk.push.enums.PushFailedReasonEnum;
import com.wm.wmim.connect.ws.api.rpc.PushWsMessageRpcService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.dubbo.config.annotation.DubboService;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author weiming
 * @date 2025/6/11 17:53
 * @description
 */
@Slf4j
@DubboService(version = "1.0.0")
public class PushSdkServiceImpl implements PushSdkService {
    @Resource
    private RouteManager routeManager;

    @Override
    public RpcRespDTO<PushWsMessageRespBO> pushMessage(PushWsMessageReqBO reqBO) {
        String businessScene = reqBO.getBusinessScene();
        RouteGetRespBO routeInfo = routeManager.getRouteInfo(businessScene, reqBO.getUserId());
        WsPushResultEnum resultEnum = WsPushResultEnum.ALL_FAIL;
        boolean allSame = true;
        WsPushResultEnum lastResult = null;
        log.info("[PushSdkService] 消息推送 start, req={}, routeInfo={}", reqBO, routeInfo);
        if (routeInfo != null && CollectionUtils.isNotEmpty(routeInfo.getRouteInfoBOList())) {
            Set<String> pathList = routeInfo.getRouteInfoBOList().stream().map(this::getPath).collect(Collectors.toSet());
            Map<String, List<RouteInfoBO>> routeMap = routeInfo.getRouteInfoBOList().stream().collect(Collectors.groupingBy(this::getPath));
            for (String path : pathList) {
                List<RouteInfoBO> routeInfoBOS = routeMap.get(path);
                List<String> wsSessionIdList = routeInfoBOS.stream().map(routeInfoBO -> routeInfoBO.getWsSessionId()).collect(Collectors.toList());
                PushWsMessageReqBO pushWsMessageReqBO = new PushWsMessageReqBO();
                pushWsMessageReqBO.setUserId(reqBO.getUserId());
                pushWsMessageReqBO.setMessage(reqBO.getMessage());
                pushWsMessageReqBO.setBusinessScene(businessScene);
                pushWsMessageReqBO.setWsSessionIdList(wsSessionIdList);
                PushWsMessageRespBO respBO = this.pushMessageToPath(pushWsMessageReqBO, path);
                WsPushResultEnum pushResult = respBO.getPushResult();
                // 发送异常的算失败, 连接断开的算成功
                if (lastResult == null) {
                    lastResult = pushResult;
                } else if (Objects.equals(lastResult, pushResult)) {
                    allSame = false;
                    break;
                }
            }
            if (allSame && Objects.equals(lastResult, WsPushResultEnum.ALL_SUCCESS)) {
                resultEnum = WsPushResultEnum.ALL_SUCCESS;
            } else if (allSame && Objects.equals(lastResult, WsPushResultEnum.ALL_FAIL)) {
                resultEnum = WsPushResultEnum.ALL_FAIL;
            } else {
                resultEnum = WsPushResultEnum.PART_SUCCESS;
            }
        } else {
            // 没有路由信息, 说明不在线
            log.info("[PushSdkService] 消息推送 没有在线的客户端! req={}, businessScene={}", reqBO, businessScene);
            resultEnum = WsPushResultEnum.ALL_FAIL;
        }
        PushWsMessageRespBO resultBO = new PushWsMessageRespBO();
        resultBO.setPushResult(resultEnum);
        log.info("[PushSdkService] 消息推送 end, req={}, routeInfo={}, resultBO={}", reqBO, routeInfo, resultBO);
        return RpcRespDTO.buildSuccess(resultBO);
    }

    private String getPath(RouteInfoBO routeInfoBO) {
        return routeInfoBO.getIp() + ":" + routeInfoBO.getDubboPort();
    }

    private PushWsMessageRespBO pushMessageToPath(PushWsMessageReqBO pushWsMessageReqBO, String path) {
        PushWsMessageRespBO respBO = new PushWsMessageRespBO();
        try {
            // 根据路径获取对应RPC提供者
            PushWsMessageRpcService pushWsMessageRpcService = RouteRpcServiceFactory.getPushWsMessageRpcService(path);
            // 向该RPC提供者机器上推送消息
            log.info("[PushSdkService] 消息推送给具体机器 start, path={},req={}", path, pushWsMessageReqBO);
            RpcRespDTO<PushWsMessageRespBO> connectPushWsMessageRespBORpcRespDTO = pushWsMessageRpcService.pushMessageLocal(pushWsMessageReqBO);
            log.info("[PushSdkService] 消息推送给具体机器 end, path={},req={}, result={}", path, pushWsMessageReqBO, connectPushWsMessageRespBORpcRespDTO);
            PushWsMessageRespBO data = RpcRespUtils.getData(connectPushWsMessageRespBORpcRespDTO);
            WsPushResultEnum pushResult = data.getPushResult();
            // 其机器上有推送失败的, 如果是连接挂掉, 则需要删除对应的路由信息
            List<PushWsMessageFailedInfoBO> failedList = data.getFailedList();
            if (!WsPushResultEnum.ALL_SUCCESS.equals(pushResult)) {
                if (CollectionUtils.isNotEmpty(failedList)) {
                    for (PushWsMessageFailedInfoBO failedInfoBO : failedList) {
                        PushFailedReasonEnum failedReason = failedInfoBO.getFailedReason();
                        if (PushFailedReasonEnum.CONNECT_NOT_EXIST.equals(failedReason)) {
                            // 删除路由信息
                            String failWsSessionId = failedInfoBO.getFailWsSessionId();
                            RouteDeleteReqBO deleteReqBO = RouteDeleteReqBO.builder().userId(pushWsMessageReqBO.getUserId()).businessScene(pushWsMessageReqBO.getBusinessScene()).wsSessionId(failWsSessionId).build();
                            routeManager.deleteRoute(deleteReqBO);
                        } else if (PushFailedReasonEnum.SEND_EXCEPTION.equals(failedReason)){
                            // TODO 发送异常的, 应该返回原因给IM 或 connect自己重新推送
                            String failWsSessionId = failedInfoBO.getFailWsSessionId();


                        }
                    }
                }
            }
            respBO.setPushResult(pushResult);
            respBO.setFailedList(failedList);
        } catch (Exception e) {
            // TODO 如果异常是对应提供者没了, 那么应该删除所有该机器上的路由信息
            log.error("[PushMessage] 推送消息异常! req={},path={},businessScene={}", pushWsMessageReqBO, path, e);
            respBO.setPushResult(WsPushResultEnum.ALL_FAIL);
        }
        return respBO;
    }
}
